<template>
  <div class="voice-recognition">
    <button @click="startRecognition" :disabled="isRecognizing">
      <el-icon v-if="isRecognizing" class="loading-icon"><Loading /></el-icon>
      <el-icon v-else><Microphone /></el-icon>
    </button>
<!--    <div v-if="result" class="result">-->
<!--      识别结果: {{ result }}-->
<!--    </div>-->
    <div v-if="error" class="error">
      错误: {{ error }}
    </div>
  </div>
</template>

<script>
import axios from 'axios';
import { Microphone, Loading } from '@element-plus/icons-vue';

export default {
  name: 'BaiDuVoiceRecognition',
  components: {
    Microphone,
    Loading
  },
  data() {
    return {
      isRecognizing: false,
      result: '',
      error: '',
      mediaRecorder: null,
      audioChunks: [],
      audioContext: null
    };
  },
  methods: {
    async startRecognition() {
      // 1. 禁用按钮防止重复点击
      this.isRecognizing = true;
      this.result = '';
      this.error = '';
      this.audioChunks = [];
      this.audioContext = new (window.AudioContext || window.webkitAudioContext)();

      try {
        // 2. 请求麦克风权限
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });

        // 3. 创建 MediaRecorder
        this.mediaRecorder = new MediaRecorder(stream);

        // 4. 设置数据收集事件
        this.mediaRecorder.ondataavailable = (event) => {
          if (event.data.size > 0) {
            this.audioChunks.push(event.data);
          }
        };

        // 5. 设置录音停止事件
        this.mediaRecorder.onstop = async () => {
          try {
            // 6. 合并音频块
            const rawAudioBlob = new Blob(this.audioChunks, { type: 'audio/webm' });

            // 7. 转换为 ArrayBuffer
            const rawArrayBuffer = await rawAudioBlob.arrayBuffer();

            // 8. 解码为 AudioBuffer
            const audioBuffer = await this.audioContext.decodeAudioData(rawArrayBuffer);

            // 9. 转换为 WAV 16kHz 单声道 PCM
            const pcmWavBlob = await this.convertToWav16kHz(audioBuffer);

            // 10. 转换为 Base64
            const base64Audio = this.arrayBufferToBase64(await pcmWavBlob.arrayBuffer());

            // 11. 调用百度云 API 进行识别
            const response = await axios.post('/voice-recognition/api/voice/recognize', {
              audioData: base64Audio,
              sampleRate: 16000,
              format: 'wav'
            }, { responseType: 'text' });

            // 12. 获取识别结果
            this.result = response.data;

            // 13. 触发 recognized 事件，将结果传递给父组件
            this.$emit('recognized', response.data);

          } catch (apiError) {
            this.error = `API调用失败: ${apiError.message}`;
          } finally {
            // 无论成功或失败，都重置状态
            this.isRecognizing = false;
          }
        };

        // 14. 开始录音
        this.mediaRecorder.start();

        // 15. 设置定时停止（示例：3秒后停止）
        setTimeout(() => {
          if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
            this.mediaRecorder.stop();
            this.mediaRecorder.stream.getTracks().forEach(track => track.stop());
          }
        }, 3000);

      } catch (error) {
        // 16. 处理错误（如权限被拒绝）
        this.error = `初始化失败: ${error.message}`;
        // 重置状态
        this.isRecognizing = false;
      }
    },

    // 核心：将 AudioBuffer 转换为 WAV 16kHz 单声道 PCM Blob
    async convertToWav16kHz(audioBuffer) {
      return new Promise((resolve) => {
        // 1. 确保单声道（如果原始是立体声，取左声道）
        const numChannels = 1;
        const originalLength = audioBuffer.length;
        const sampleRate = 16000; // 目标采样率
        const bitDepth = 16; // 16-bit PCM

        // 计算重采样后的样本数（线性插值）
        const newLength = Math.round(originalLength * (sampleRate / audioBuffer.sampleRate));
        const newData = new Float32Array(newLength);

        // 线性插值重采样（简单实现，可优化）
        for (let i = 0; i < newLength; i++) {
          const originalIndex = Math.floor(i * (audioBuffer.sampleRate / sampleRate));
          newData[i] = audioBuffer.getChannelData(0)[originalIndex]; // 取左声道
        }

        // 2. 转换为 16-bit PCM 整数（范围：-32768 到 32767）
        const pcmData = new Int16Array(newData.length);
        for (let i = 0; i < newData.length; i++) {
          pcmData[i] = Math.max(-32768, Math.min(32767, Math.floor(newData[i] * 32767)));
        }

        // 3. 生成 WAV 头
        const wavHeader = this.createWavHeader(pcmData.length, sampleRate, numChannels, bitDepth);

        // 4. 合并头和数据
        const wavBlob = new Blob([wavHeader, pcmData.buffer], { type: 'audio/wav' });
        resolve(wavBlob);
      });
    },

    // 生成 WAV 头（简化版）
    createWavHeader(dataLength, sampleRate, numChannels, bitDepth) {
      const header = new Uint8Array(44);

      // RIFF 标识
      header[0] = 'R'.charCodeAt(0);
      header[1] = 'I'.charCodeAt(0);
      header[2] = 'F'.charCodeAt(0);
      header[3] = 'F'.charCodeAt(0);

      // 文件大小（RIFF块总大小）
      const fileSize = 36 + dataLength;
      header[4] = (fileSize & 0xFF);
      header[5] = ((fileSize >> 8) & 0xFF);
      header[6] = ((fileSize >> 16) & 0xFF);
      header[7] = ((fileSize >> 24) & 0xFF);

      // WAVE 标识
      header[8] = 'W'.charCodeAt(0);
      header[9] = 'A'.charCodeAt(0);
      header[10] = 'V'.charCodeAt(0);
      header[11] = 'E'.charCodeAt(0);

      // fmt 子块标识
      header[12] = 'f'.charCodeAt(0);
      header[13] = 'm'.charCodeAt(0);
      header[14] = 't'.charCodeAt(0);
      header[15] = ' '.charCodeAt(0);

      // fmt 子块大小（固定 16）
      header[16] = 16;
      header[17] = 0;
      header[18] = 0;
      header[19] = 0;

      // 音频格式（PCM = 1）
      header[20] = 1;
      header[21] = 0;

      // 声道数
      header[22] = numChannels;
      header[23] = 0;

      // 采样率
      header[24] = sampleRate & 0xFF;
      header[25] = (sampleRate >> 8) & 0xFF;
      header[26] = (sampleRate >> 16) & 0xFF;
      header[27] = (sampleRate >> 24) & 0xFF;

      // 字节率（采样率 * 声道数 * 位深 / 8）
      const byteRate = sampleRate * numChannels * bitDepth / 8;
      header[28] = byteRate & 0xFF;
      header[29] = (byteRate >> 8) & 0xFF;
      header[30] = (byteRate >> 16) & 0xFF;
      header[31] = (byteRate >> 24) & 0xFF;

      // 块对齐（声道数 * 位深 / 8）
      const blockAlign = numChannels * bitDepth / 8;
      header[32] = blockAlign & 0xFF;
      header[33] = (blockAlign >> 8) & 0xFF;

      // 每个样本的位数（16-bit）
      header[34] = bitDepth;
      header[35] = 0;

      // data 子块标识
      header[36] = 'd'.charCodeAt(0);
      header[37] = 'a'.charCodeAt(0);
      header[38] = 't'.charCodeAt(0);
      header[39] = 'a'.charCodeAt(0);

      // data 子块大小（PCM数据长度）
      header[40] = dataLength & 0xFF;
      header[41] = (dataLength >> 8) & 0xFF;
      header[42] = (dataLength >> 16) & 0xFF;
      header[43] = (dataLength >> 24) & 0xFF;

      return header;
    },

    // 将 ArrayBuffer 转换为 Base64
    arrayBufferToBase64(buffer) {
      let binary = '';
      const bytes = new Uint8Array(buffer);
      const len = bytes.byteLength;
      for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
      }
      return window.btoa(binary);
    }
  },
  beforeUnmount() {
    if (this.audioContext) {
      this.audioContext.close();
    }
  }
}
</script>

<style scoped>
.voice-recognition {
  display: flex;
  align-items: center;
  margin-right: 10px;
}

button {
  padding: 10px 20px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.result, .error {
  margin-top: 10px;
  font-size: 14px;
}

.error {
  color: red;
}

.loading-icon {
  animation: spin 1s linear infinite;
}

@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
</style>